#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""
配置验证器 - 确保配置文件的正确性和一致性

在系统启动时验证所有配置，避免运行时错误
"""

import logging
from typing import Dict, Any, List

logger = logging.getLogger('ConfigValidator')


class ConfigValidator:
    """配置验证器"""
    
    def __init__(self, config: Dict[str, Any]):
        """
        初始化验证器
        
        Args:
            config: 从 config.yaml 加载的配置字典
        """
        self.config = config
        self.errors = []
        self.warnings = []
    
    def validate_all(self) -> bool:
        """
        执行所有验证
        
        Returns:
            bool: 验证是否通过
        """
        print("\n" + "="*60)
        print("🔍 开始配置验证...")
        print("="*60)
        
        # 执行各项验证
        self._validate_position_calculator()
        self._validate_trade_protector()
        self._validate_strategy()
        self._validate_market_cap()
        self._validate_buy_params()
        self._validate_sell_params()
        self._validate_backtest()
        self._validate_stop_loss()
        
        # 输出结果
        self._print_results()
        
        return len(self.errors) == 0
    
    def _validate_position_calculator(self):
        """验证仓位计算器配置"""
        try:
            pc = self.config['position_calculator']
            
            # 1. 基准仓位范围
            base_position = pc['base_position']
            if not (0 < base_position <= 1.0):
                self.errors.append(
                    f"仓位计算器：base_position={base_position} 超出有效范围 (0, 1]"
                )
            
            # 2. 价格位置系数
            price_coef = pc['price_position_coef']
            if price_coef['low'] <= 0 or price_coef['high'] <= 0:
                self.errors.append(
                    "仓位计算器：价格位置系数必须为正数"
                )
            
            # 3. 批次比例之和应为1
            batch_ratios = pc['batch_ratios']
            total_ratio = batch_ratios['first'] + batch_ratios['second']
            if abs(total_ratio - 1.0) > 0.01:  # 允许0.01的误差
                self.warnings.append(
                    f"仓位计算器：首批({batch_ratios['first']}) + "
                    f"第二批({batch_ratios['second']}) = {total_ratio} ≠ 1.0"
                )
            
            # 4. 加仓条件逻辑
            add_conditions = pc['add_position_conditions']
            if add_conditions['min_price_increase'] >= add_conditions['max_price_increase']:
                self.errors.append(
                    f"仓位计算器：最小涨幅({add_conditions['min_price_increase']}%) "
                    f"应小于最大涨幅({add_conditions['max_price_increase']}%)"
                )
            
            # 5. 减仓比例检查
            sell_ratios = pc['sell_batch_ratios']
            for batch, ratio in sell_ratios.items():
                if not (0 < ratio <= 1.0):
                    self.errors.append(
                        f"仓位计算器：减仓比例 {batch}={ratio} 超出范围 (0, 1]"
                    )
            
            logger.info("✅ 仓位计算器配置验证通过")
            
        except KeyError as e:
            self.errors.append(f"仓位计算器：缺少必需配置项 {e}")
        except Exception as e:
            self.errors.append(f"仓位计算器：验证异常 {e}")
    
    def _validate_trade_protector(self):
        """验证交易保护器配置"""
        try:
            tp = self.config['trade_protector']
            
            # 1. 最小持仓时间
            min_holding = tp['min_holding_minutes']
            if min_holding < 0:
                self.errors.append(
                    f"交易保护器：min_holding_minutes={min_holding} 不能为负数"
                )
            elif min_holding < 5:
                self.warnings.append(
                    f"交易保护器：min_holding_minutes={min_holding} 过短，"
                    "可能导致过度交易"
                )
            
            # 2. 冷静期
            cooldown = tp['repeat_buy_cooldown_hours']
            if cooldown < 0:
                self.errors.append(
                    f"交易保护器：repeat_buy_cooldown_hours={cooldown} 不能为负数"
                )
            
            # 3. 跌破MA5最小幅度
            min_break = tp['min_break_pct']
            if not (0 <= min_break <= 10):
                self.warnings.append(
                    f"交易保护器：min_break_pct={min_break}% 可能不合理（建议0-10%）"
                )
            
            logger.info("✅ 交易保护器配置验证通过")
            
        except KeyError as e:
            self.errors.append(f"交易保护器：缺少必需配置项 {e}")
        except Exception as e:
            self.errors.append(f"交易保护器：验证异常 {e}")
    
    def _validate_strategy(self):
        """验证策略配置"""
        try:
            strategy = self.config['strategy']
            
            # 1. 价格位置阈值
            threshold = strategy['price_position_threshold']
            if not (0 < threshold <= 1.0):
                self.errors.append(
                    f"策略配置：price_position_threshold={threshold} 超出范围 (0, 1]"
                )
            
            # 2. 必需的子策略
            if 'normal' not in strategy or 'strong_stock' not in strategy:
                self.errors.append(
                    "策略配置：缺少 'normal' 或 'strong_stock' 子策略配置"
                )
            
            # 3. 评分系统（如果启用）
            if 'normal' in strategy and 'scoring' in strategy['normal']:
                scoring = strategy['normal']['scoring']
                total_score = (
                    scoring.get('base_score', 0) +
                    scoring.get('volume_score', 0) +
                    scoring.get('amplitude_score', 0) +
                    scoring.get('position_score', 0)
                )
                
                pass_threshold = scoring.get('pass_threshold', 0)
                if pass_threshold > total_score:
                    self.errors.append(
                        f"策略配置：通过阈值({pass_threshold}分) 大于总分({total_score}分)"
                    )
            
            logger.info("✅ 策略配置验证通过")
            
        except KeyError as e:
            self.errors.append(f"策略配置：缺少必需配置项 {e}")
        except Exception as e:
            self.errors.append(f"策略配置：验证异常 {e}")
    
    def _validate_market_cap(self):
        """验证市值分类配置"""
        try:
            mc = self.config['market_cap_threshold']
            
            # 1. 必需的字段
            if 'large' not in mc or 'small' not in mc:
                self.errors.append(
                    "市值配置：缺少 'large' 或 'small' 阈值"
                )
                return
            
            # 2. 逻辑一致性：large > small
            if mc['large'] <= mc['small']:
                self.errors.append(
                    f"市值配置：large({mc['large']}亿) 必须大于 small({mc['small']}亿)\n"
                    f"当前配置会导致分类逻辑错误"
                )
            
            # 3. 合理性检查
            if mc['small'] < 10:
                self.warnings.append(
                    f"市值配置：small={mc['small']}亿 过小，A股小盘股建议≥20亿"
                )
            
            if mc['large'] < 50:
                self.warnings.append(
                    f"市值配置：large={mc['large']}亿 过小，A股大盘股建议≥200亿"
                )
            
            logger.info("✅ 市值配置验证通过")
            
        except KeyError as e:
            self.errors.append(f"市值配置：缺少必需配置项 {e}")
        except Exception as e:
            self.errors.append(f"市值配置：验证异常 {e}")
    
    def _validate_buy_params(self):
        """验证买入参数配置"""
        try:
            # 验证低位标准策略
            normal_vol = self.config.get('normal_strategy_volume', {})
            for cap_type in ['large', 'mid', 'small']:
                if cap_type not in normal_vol:
                    self.errors.append(
                        f"买入参数：normal_strategy_volume 缺少 '{cap_type}' 配置"
                    )
                    continue
                
                # 检查量能分类的逻辑顺序
                vol_config = normal_vol[cap_type]
                required_keys = ['strong', 'moderate', 'normal', 'shrink']
                for key in required_keys:
                    if key not in vol_config:
                        self.errors.append(
                            f"买入参数：normal_strategy_volume.{cap_type} 缺少 '{key}'"
                        )
            
            # 验证高位强势策略
            strong_strat = self.config.get('strong_stock_strategy', {})
            required_sections = ['ma_spacing', 'volume_ratio', 'volume_position']
            for section in required_sections:
                if section not in strong_strat:
                    self.errors.append(
                        f"买入参数：strong_stock_strategy 缺少 '{section}' 配置"
                    )
            
            logger.info("✅ 买入参数配置验证通过")
            
        except Exception as e:
            self.errors.append(f"买入参数：验证异常 {e}")
    
    def _validate_sell_params(self):
        """验证卖出参数配置"""
        try:
            required_conditions = [
                'sell_high_shrink',
                'sell_extreme_volume',
                'sell_volume_lag',
                'sell_break_ma5'
            ]
            
            for condition in required_conditions:
                if condition not in self.config:
                    self.errors.append(
                        f"卖出参数：缺少 '{condition}' 配置"
                    )
                    continue
                
                # 检查每种卖出条件是否包含大中小盘配置
                sell_config = self.config[condition]
                for cap_type in ['large', 'mid', 'small']:
                    if cap_type not in sell_config:
                        self.errors.append(
                            f"卖出参数：{condition} 缺少 '{cap_type}' 配置"
                        )
            
            logger.info("✅ 卖出参数配置验证通过")
            
        except Exception as e:
            self.errors.append(f"卖出参数：验证异常 {e}")
    
    def _validate_backtest(self):
        """验证回测配置"""
        try:
            bt = self.config['backtest']
            
            # 1. 初始资金
            initial_capital = bt['initial_capital']
            if initial_capital <= 0:
                self.errors.append(
                    f"回测配置：initial_capital={initial_capital} 必须为正数"
                )
            elif initial_capital < 10000:
                self.warnings.append(
                    f"回测配置：initial_capital={initial_capital} 过小，建议≥10万"
                )
            
            # 2. 手续费率
            commission = bt['commission_rate']
            if not (0 <= commission <= 0.01):  # 0-1%
                self.warnings.append(
                    f"回测配置：commission_rate={commission*10000}‰ 可能不合理（A股通常万1-万5）"
                )
            
            # 3. 最大持仓数
            max_positions = bt['max_positions']
            if max_positions <= 0:
                self.errors.append(
                    f"回测配置：max_positions={max_positions} 必须为正数"
                )
            elif max_positions > 10:
                self.warnings.append(
                    f"回测配置：max_positions={max_positions} 过大，可能导致过度分散"
                )
            
            # 4. 滑点配置逻辑
            slippage = bt['slippage']
            for cap_type in ['large_cap', 'mid_cap', 'small_cap']:
                if cap_type not in slippage:
                    self.errors.append(
                        f"回测配置：slippage 缺少 '{cap_type}' 配置"
                    )
                    continue
                
                # 高波动滑点应该 > 默认滑点
                default = slippage[cap_type].get('default', 0)
                high_vol = slippage[cap_type].get('high_volatility', 0)
                
                if high_vol < default:
                    self.errors.append(
                        f"回测配置：{cap_type} 的高波动滑点({high_vol}) "
                        f"应大于默认滑点({default})"
                    )
            
            # 5. 滑点合理性（小盘 > 中盘 > 大盘）
            if all(cap in slippage for cap in ['large_cap', 'mid_cap', 'small_cap']):
                large_default = slippage['large_cap']['default']
                mid_default = slippage['mid_cap']['default']
                small_default = slippage['small_cap']['default']
                
                if not (large_default <= mid_default <= small_default):
                    self.warnings.append(
                        f"回测配置：滑点应遵循 大盘≤中盘≤小盘 的规律\n"
                        f"当前：大盘={large_default}, 中盘={mid_default}, 小盘={small_default}"
                    )
            
            logger.info("✅ 回测配置验证通过")
            
        except KeyError as e:
            self.errors.append(f"回测配置：缺少必需配置项 {e}")
        except Exception as e:
            self.errors.append(f"回测配置：验证异常 {e}")
    
    def _validate_stop_loss(self):
        """验证止损配置（如果存在）"""
        try:
            if 'stop_loss' not in self.config:
                logger.info("⊘ 未配置止损功能（可选）")
                return
            
            sl = self.config['stop_loss']
            
            # 1. 止损策略
            strategy = sl.get('strategy', 'atr_trailing')
            valid_strategies = ['fixed', 'atr_trailing', 'percentage']
            if strategy not in valid_strategies:
                self.errors.append(
                    f"止损配置：strategy='{strategy}' 无效，"
                    f"应为 {valid_strategies} 之一"
                )
            
            # 2. 固定止损阈值
            if 'threshold' in sl:
                threshold = sl['threshold']
                if not (0 < threshold <= 0.5):  # 0-50%
                    self.warnings.append(
                        f"止损配置：threshold={threshold*100}% 可能不合理（建议3-20%）"
                    )
            
            # 3. ATR参数
            if strategy == 'atr_trailing':
                atr_period = sl.get('atr_period', 14)
                atr_multiplier = sl.get('atr_multiplier', 2.0)
                
                if atr_period < 5 or atr_period > 50:
                    self.warnings.append(
                        f"止损配置：atr_period={atr_period} 可能不合理（建议10-30）"
                    )
                
                if atr_multiplier < 1.0 or atr_multiplier > 5.0:
                    self.warnings.append(
                        f"止损配置：atr_multiplier={atr_multiplier} 可能不合理（建议1.5-3.0）"
                    )
            
            logger.info("✅ 止损配置验证通过")
            
        except Exception as e:
            self.errors.append(f"止损配置：验证异常 {e}")
    
    def _print_results(self):
        """打印验证结果"""
        print("\n" + "="*60)
        
        if self.errors:
            print("❌ 配置验证失败！发现以下错误：")
            print("="*60)
            for i, error in enumerate(self.errors, 1):
                print(f"\n[错误 {i}] {error}")
            print("\n" + "="*60)
            print("⚠️  请修正配置文件后重试")
        else:
            print("✅ 配置验证通过！")
            
        if self.warnings:
            print("\n" + "-"*60)
            print("⚠️  发现以下警告（不影响运行）：")
            print("-"*60)
            for i, warning in enumerate(self.warnings, 1):
                print(f"\n[警告 {i}] {warning}")
        
        print("\n" + "="*60)


def validate_config(config: Dict[str, Any]) -> bool:
    """
    验证配置文件
    
    Args:
        config: 配置字典
    
    Returns:
        bool: 验证是否通过
    
    Raises:
        ValueError: 如果配置验证失败
    """
    validator = ConfigValidator(config)
    is_valid = validator.validate_all()
    
    if not is_valid:
        raise ValueError(
            "配置文件验证失败，请检查上述错误并修正后重启程序"
        )
    
    return True


if __name__ == '__main__':
    # 测试配置验证
    from config_loader import get_config_loader
    
    print("\n" + "🔍 测试配置验证器")
    
    try:
        config = get_config_loader()._config
        validate_config(config)
        print("\n✅ 当前配置文件验证通过！")
    except ValueError as e:
        print(f"\n❌ 配置验证失败：\n{e}")
    except Exception as e:
        print(f"\n❌ 验证过程出错：\n{e}")
        import traceback
        traceback.print_exc()

